//
// Created by Administrator on 2024/11/11.
//

#include "DicomImage.h"
#include "../constant/StringUtils.h"
#include "Configuration.h"
#include "DcmToolkit.h"
#include <boost/algorithm/string.hpp>
#include <numeric>
#include <format>



DicomInstance DicomImage::buildInstance(const std::map<std::string, std::string> &d) {

    std::string slope_str = d.at("RescaleSlope");
    std::string intercept_str = d.at("RescaleIntercept");

    float rescale_slope, rescale_intercept;
    auto [p_slope, ec_slope] = std::from_chars(slope_str.data(), slope_str.data() + slope_str.size(), rescale_slope);
    auto [p_intercept, ec_intercept] = std::from_chars(intercept_str.data(),
                                                       intercept_str.data() + intercept_str.size(), rescale_intercept);
    if (!(ec_slope == std::errc() && ec_intercept == std::errc())) {
        throw std::runtime_error("Error converting RescaleSlope or RescaleIntercept. ");
    }

    std::vector<float> pixel_spacing_float;
    auto pixel_spacing = StringUtils::split(d.at("PixelSpacing"), '/');
    for (const auto &f: pixel_spacing) {
        float v;
        auto [p, ec] = std::from_chars(f.data(), f.data() + f.size(), v);
        if (ec != std::errc()) {
            throw std::runtime_error("Error converting PixelSpacing. ");
        }
        pixel_spacing_float.push_back(v);
    }

    return {
            d.at("BitsAllocated"),
            false,
            d.at("BitsStored"),
            d.at("Columns"),
            d.at("fileName"),
            static_cast<int>(d.at("fileName").size()),
            d.at("HighBit"),
            StringUtils::split(d.at("ImageOrientationPatient"), '/'),
            StringUtils::split(d.at("ImagePositionPatient"), '/'),
            StringUtils::split(d.at("ImageType"), '/'),
            d.at("Modality"),
            d.at("InstanceNumber"),
            d.at("PhotometricInterpretation"),
            pixel_spacing_float,
            rescale_intercept,
            rescale_slope,
            d.at("Rows"),
            d.at("SliceThickness"),
            d.at("SOPInstanceUID"),
            d.at("WindowCenter"),
            d.at("WindowWidth"),
    };
}

DicomSeries DicomImage::buildSeries(const std::map<std::string, std::string> &d, const std::vector<DicomInstance> &inst) {
    char* end;
    return {
            d.contains("BodyPartExamined")?d.at("BodyPartExamined"):"",
            d.at("SeriesDate"),
            d.at("SeriesDescription"),
            inst,
            d.at("InstitutionName"),
            d.at("Manufacturer"),
            d.at("ManufacturerModelName"),
            d.at("StationName"),
            static_cast<int>(std::strtol(d.at("SeriesNumber").c_str(), &end, 10)),
            "",
            d.at("SeriesTime"),
            d.at("SeriesInstanceUID"),

    };
}

DicomStudy DicomImage::buildStudy(const std::map<std::string, std::string> &d) {
    Configuration &config = Configuration::getInstance();
    auto bucketName = config.getS3BucketName();
    auto endpoint = config.getS3endpoint();
    auto rootPath = DcmToolKit::getObjKeyByPrefix(d);
    return {
            rootPath,
            bucketName,
            d.at("AccessionNumber"),
            d.at("StudyDate"),
            d.at("StudyDescription"),
            d.at("StudyID"),
            d.at("PatientAge"),
            d.at("PatientBirthDate"),
            d.at("PatientSex"),
            d.at("PatientID"),
            d.at("PatientName"),
            d.at("pipeline"),
            std::vector<DicomSeries>{},
            d.at("StudyTime"),
            d.at("StudyInstanceUID"),
            d.at("TransferSyntaxUID"),
            {},
            d.at("Modality"),
            0,
            0,
            "",
            "",
           0,
            d.at("ManufacturerModelName"),
            d.at("StationName"),
            "",
            "",
            d.contains("BodyPartExamined")?d.at("BodyPartExamined"):"",
            endpoint,
            d.at("installCode"),

    };
}



DicomStudy DicomImage::addSeriesToStudy(const std::vector<std::map<std::string, std::string>> &images) {
    auto study = buildStudy(images[0]);
    std::map<std::string, std::vector<DicomInstance>> dict;
    std::map<std::string, std::map<std::string, std::string>> series;
    std::vector<int> vec;
    for (const auto &img: images) {
        auto key = img.at("SeriesInstanceUID");
        auto inst = buildInstance(img);
        vec.push_back(static_cast<int>(img.at("fileName").size()));
        if (dict.count(key) > 0) {
            dict.at(key).push_back(inst);
        } else {
            dict.insert({key, {inst}});
            series.insert({key, img});
        }
    }

    for (const auto &d: dict) {
        auto serInst = buildSeries(series.at(d.first), d.second);
        study.series.push_back(serInst);
    }

    std::sort(study.series.begin(), study.series.end(), [](const DicomSeries& a, const DicomSeries& b) {
        return a.number > b.number;
    });


    study.seriesCount = static_cast<int>(study.series.size());
    study.instanceCount = static_cast<int>(images.size());
    study.storageSize = std::accumulate(vec.begin(), vec.end(), 0);
    return study;
}
